#include "controlPoints.h"

controlPoints::controlPoints(gObjectContainer *cont):
	container(cont), matrix(NULL), activeGObject(NULL), activeGTypeGo(NULL),
	clickedGObject(NULL), clickedGTypeGo(NULL), doubleClickedGObject(NULL),
	press(false), wasClicked(false){}

bool controlPoints::isIn(const QPointF &mouse, const QPointF &point) const{

	QPointF mapPoint(matrix->map(point));
	
	if(
		mouse.x() > mapPoint.x() - POINT_SIZE &&
		mouse.x() < mapPoint.x() + POINT_SIZE &&
		mouse.y() > mapPoint.y() - POINT_SIZE &&
		mouse.y() < mapPoint.y() + POINT_SIZE
		)
		return true;
	return false;

}

float controlPoints::getDistance(QPointF &a, QPointF &b) const{

	float da = b.x() - a.x();
	float db = b.y() - a.y();
	
	return sqrt(da * da + db * db); 

}

float controlPoints::getHigh(QPointF &prev, QPointF &go, QPointF &mouse) const{

	float a = getDistance(prev, go);
	float b = getDistance(go, mouse);
	float c = getDistance(mouse, prev);
	float a1 = (c * c - b * b) / (2 * a) + a / 2;
	
	if(a1 < 0 || a1 > a)
		return std::numeric_limits<float>::max();
	return sqrt(c * c - a1 * a1);

}

bool controlPoints::circlePrimitive(
	QPointF &prev,
	QPointF &go,
	QPointF &S,
	QPointF &mouse,
	int type) const{

	float origR = getDistance(S, go);
	float r = getDistance(S, mouse);
	
	if(origR - PRIMITIVE_DISTANCE > r || origR + PRIMITIVE_DISTANCE < r)
		return false;
	
	float angleA = atan2(prev.y() - S.y(), prev.x() - S.x());
	float angleB = atan2(go.y() - S.y(), go.x() - S.x());
	float angleC = atan2(mouse.y() - S.y(), mouse.x() - S.x());
	
	if(angleB > angleA){
		if(angleB < angleC || angleC < angleA){
			if(type == TYPE_ARC_COUNTER)
				return true;
		}
		else if(type == TYPE_ARC_CLOCK)
			return true;
	}
	else{
		if(angleB < angleC && angleC < angleA){
			if(type == TYPE_ARC_COUNTER)
				return true;
		}
		else if(type == TYPE_ARC_CLOCK)
			return true;
	}
	return false;

}

bool controlPoints::primitiveIntersect(gTypeGo &prev, gTypeGo &go, QPointF &point) const{

	QPointF pointPrev(prev[0], prev[1]);
	QPointF pointGo(go[0], go[1]);
	
	pointPrev = matrix->map(pointPrev);
	pointGo = matrix->map(pointGo);
	if(go.getTypeValue() == TYPE_LINEAR_MOVE ||
		go.getTypeValue() == TYPE_LINEAR_CUT){
		
		if(getHigh(pointPrev, pointGo, point) < PRIMITIVE_DISTANCE)
			return true;
	}
	else if(go.getTypeValue() == TYPE_ARC_COUNTER ||
		go.getTypeValue() == TYPE_ARC_CLOCK){
		
		QPointF S(prev[0] + go[3], prev[1] + go[4]);
		
		S = matrix->map(S);
		if(circlePrimitive(pointPrev, pointGo, S, point, go.getTypeValue()))
			return true;
	}
	return false;

}

bool controlPoints::intersect(QPointF &mouse){

	gObjectContainer::iterator iterContainer;
	gObject::iterator iterObject;
	QPointF point;
	gTypeGo *buff = 0;
	gTypeGo *go;
	bool flag = false;
	
	if(clickedGTypeGo != NULL)
		if(isIn(mouse, clickedPoint)){
			activeGTypeGo = clickedGTypeGo;
			activeGObject = clickedGObject;
			activePoint = clickedPoint;
			return true;
		}
	if(doubleClickedGObject){
		iterContainer = container->begin() + container->getIndexByObject(doubleClickedGObject);
		iterObject = doubleClickedGObject->begin();
		for(; iterObject != (*iterContainer)->end(); iterObject++){
			go = dynamic_cast<gTypeGo *>(*iterObject);
			point.setX((*go)[0]);
			point.setY((*go)[1]);
			if(isIn(mouse, point))
				flag = true;
			else if((*iterContainer)->begin() != iterObject)	
					if(buff && primitiveIntersect(*buff, *go, mouse))
						flag = true;
			buff = go;
			if(flag){
				activeGObject = *iterContainer;
				activeGTypeGo = go;
				activePoint = point;
				return true;
			}	
		}
	}
	iterContainer = container->begin();
	for(; iterContainer != container->end(); iterContainer++){
		iterObject = (*iterContainer)->begin();
		if(*iterContainer == doubleClickedGObject)
			continue;
		for(; iterObject != (*iterContainer)->end(); iterObject++){
			go = dynamic_cast<gTypeGo *>(*iterObject);
			point.setX((*go)[0]);
			point.setY((*go)[1]);
			if(isIn(mouse, point))
				flag = true;
			else if((*iterContainer)->begin() != iterObject)	
					if(buff && primitiveIntersect(*buff, *go, mouse))
						flag = true;
			buff = go;
			if(flag){
				activeGObject = *iterContainer;
				activeGTypeGo = go;
				activePoint = point;
				return true;
			}	
		}
	}
	activeGTypeGo = NULL;
	return false;	

}

void controlPoints::pressed(QPointF &point){

	buffPoint = point;
	press = true;

}

float controlPoints::distance(QPointF &p1, QPointF &p2){

	return (p2.x() - p1.x()) * (p2.x() - p1.x()) + (p2.y() - p1.y()) * (p2.y() - p1.y());

}

void controlPoints::doubleClicked(QPointF &){

	if(isActive()){
		clickedGTypeGo = NULL;
		doubleClickedGObject = activeGObject;
	}
	else
		doubleClickedGObject = NULL;

}

void controlPoints::doubleClicked(gObject *object){

	doubleClickedGObject = object;

}

bool controlPoints::isDoubleClicked() const{

	return doubleClickedGObject == NULL ? false : true;

}

void controlPoints::clicked(QPointF &point){

	if(isActive()){
		clickedGObject = activeGObject;
		clickedGTypeGo = activeGTypeGo;
		clickedPoint = activePoint;
		wasClicked = true;
	}
	else if(distance(point, buffPoint) < MINIMAL_MOVE_DISTANCE){
        clickedGTypeGo = NULL;
		wasClicked = true;
	}
	else
		wasClicked = false;
	press = false;	
		
}

void controlPoints::clicked(gObject *object, gTypeGo *go){
	
	
	clickedGObject = object;
	clickedGTypeGo = go;
	if(!go)
		wasClicked = false;

}

bool controlPoints::isWasClicked(){

	return wasClicked;

}

bool controlPoints::isClicked() const{

	return clickedGTypeGo == NULL ? false : true;

}

void controlPoints::setMatrix(QMatrix *m){

	matrix = m;

}

bool controlPoints::isActive() const{

	return activeGTypeGo == NULL ? false : true;

}

gObject *controlPoints::getActiveGObject() const{

	return activeGObject;

}

gTypeGo *controlPoints::getActiveGTypeGo() const{

	return activeGTypeGo;

}

const QPointF &controlPoints::getActivePoint() const{

	return activePoint;

}

gObject *controlPoints::getClickedGObject() const{

	return clickedGObject;

}

gObject *controlPoints::getDoubleClickedGObject() const{

	return doubleClickedGObject;

}

gTypeGo *controlPoints::getClickedGTypeGo() const{

	return clickedGTypeGo;

}

const QPointF &controlPoints::getClickedPoint() const{

	return clickedPoint;

}


const QPointF &controlPoints::getPressedPoint() const{

	return buffPoint;

}

bool controlPoints::isPressed(){

	return press;

}

void controlPoints::setMiddle(){

	int i;

	if(activeGTypeGo != NULL)
		if(activeGTypeGo->getTypeValue() == TYPE_ARC_CLOCK ||
			activeGTypeGo->getTypeValue() == TYPE_ARC_COUNTER){
				i = activeGObject->getIndexByObject(activeGTypeGo);
				gTypeGo &go = dynamic_cast<gTypeGo &>(*activeGObject->at(i - 1));
				gTypeGoCircle goCircle = dynamic_cast<gTypeGoCircle &>(*activeGTypeGo);
				clickedGTypeGo = activeGTypeGo;
				clickedGObject = activeGObject;
				clickedPoint.setX(go[0] + goCircle[3]);
				clickedPoint.setY(go[1] + goCircle[4]);
		}

}

bool controlPoints::isMiddle(){
	
	float P[2];
	
	P[0] = clickedPoint.x();
	P[1] = clickedPoint.y();
	if(isClicked())
		if(clickedGTypeGo->getTypeValue() == TYPE_ARC_CLOCK ||
			clickedGTypeGo->getTypeValue() == TYPE_ARC_COUNTER)
			if(!COMPARE(*clickedGTypeGo, P))
				return true;
	return false;
	
}

void controlPoints::update(){
	
	if(isActive()){
		activePoint.setX((*activeGTypeGo)[0]);
		activePoint.setY((*activeGTypeGo)[1]);
	}
	if(isClicked()){
		clickedPoint.setX((*clickedGTypeGo)[0]);
		clickedPoint.setY((*clickedGTypeGo)[1]);
	}

}
